﻿namespace Jsl.StyleCop.CSharp
{
    using System;
    using System.Linq;
    using Microsoft.StyleCop.CSharp;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    public class FlowGraphTester
    {
        private readonly CodeUnitGraph graph;
        private ICodeUnit currentVertex;

        #region Constructors
        private FlowGraphTester(ICodeUnit root)
        {
            this.graph = FlowGraph.BuildFlowGraph(root);
            this.currentVertex = root;
        }
        #endregion Constructors

        #region Public Properties
        public ICodeUnit CurrentVertex
        {
            get { return this.currentVertex; }
            private set { this.currentVertex = value; }
        }

        public CodeUnitGraph Graph
        {
            get { return this.graph; }
        }
        #endregion Public Properties

        #region Public Methods
        public static FlowGraphTester Test(Method method)
        {
            return new FlowGraphTester(method);
        }

        public static FlowGraphTester Test(string methodCode)
        {
            return FlowGraphTester.Test(AnalysisHelper.MethodFromMethodCode(methodCode));
        }

        public FlowGraphTester InDegree(int inEdgeCount)
        {
            Assert.AreEqual(inEdgeCount, this.Graph.InDegree(this.CurrentVertex));

            return this;
        }

        public FlowGraphTester IsAtEnd()
        {
            Assert.AreEqual(0, this.Graph.OutDegree(this.CurrentVertex));

            return this;
        }

        public FlowGraphTester Vertex<TType>()
        {
            Assert.IsInstanceOfType(this.CurrentVertex, typeof(TType));

            return this;
        }

        public FlowGraphTester MoveToVertex(ICodeUnit codeUnit)
        {
            this.CurrentVertex = codeUnit;

            return this;
        }

        public FlowGraphTester MoveToNextVertex()
        {
            this.currentVertex = this.Graph.OutEdges(this.CurrentVertex).Single().Target;

            return this;
        }

        public FlowGraphTester MoveToNextVertex(int edgeIndex)
        {
            this.currentVertex = this.Graph.OutEdge(this.CurrentVertex, edgeIndex).Target;

            return this;
        }

        public FlowGraphTester MoveToNextVertex(ICodeUnit vertex, int edgeIndex)
        {
            this.currentVertex = this.Graph.OutEdge(vertex, edgeIndex).Target;

            return this;
        }

        public FlowGraphTester MoveToPreviousVertex(int edgeIndex)
        {
            this.CurrentVertex = this.Graph.InEdge(this.CurrentVertex, edgeIndex).Target;

            return this;
        }

        public FlowGraphTester OutDegree(int outEdgeCount)
        {
            Assert.AreEqual(outEdgeCount, this.Graph.OutDegree(this.CurrentVertex));

            return this;
        }

        public FlowGraphTester Vertex(Action<ICodeUnit> vertexAction)
        {
            vertexAction(this.CurrentVertex);

            return this;
        }

        public FlowGraphTester Vertex<TCodeUnit>(Action<TCodeUnit> vertexAction)
            where TCodeUnit : class
        {
            Assert.IsInstanceOfType(this.CurrentVertex, typeof(TCodeUnit));
            vertexAction((TCodeUnit)this.CurrentVertex);

            return this;
        }
        #endregion Public Methods
    }
}